Swadhin Biswas feat(api): add runner, analytics, packages, and security services · 3/6/2026
28f7954
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import type { APIRoute } from "astro";
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
import { and, eq } from "drizzle-orm";
import { z } from "zod";
import { getDatabase, schema } from "@/db";
import { badRequest, notFound, parseQuery, success } from "@/lib/api";
import { withErrorHandler } from "@/lib/errors";
import { canReadRepo } from "@/lib/permissions";
import { getDeveloperWorkloads } from "@/lib/analytics-advanced";
const querySchema = z.object({
days: z.coerce.number().int().min(7).max(365).optional(),
limit: z.coerce.number().int().min(1).max(200).optional(),
});
async function resolveRepository(owner: string, repoName: string) {
const db = getDatabase() as NodePgDatabase<typeof schema>;
const repoOwner = await db.query.users.findFirst({
where: eq(schema.users.username, owner),
});
if (!repoOwner) return null;
return db.query.repositories.findFirst({
where: and(
eq(schema.repositories.ownerId, repoOwner.id),
eq(schema.repositories.name, repoName)
),
});
}
type WorkloadRecommendation = {
priority: "high" | "medium" | "low";
title: string;
rationale: string;
action: string;
};
function round2(value: number): number {
return Math.round(value * 100) / 100;
}
function percentile(values: number[], p: number): number {
if (!values.length) return 0;
const sorted = [...values].sort((a, b) => a - b);
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1));
return sorted[index] || 0;
}
function topShare(values: number[], topN: number): number {
if (!values.length) return 0;
const sorted = [...values].sort((a, b) => b - a);
const top = sorted.slice(0, topN).reduce((sum, value) => sum + value, 0);
const total = sorted.reduce((sum, value) => sum + value, 0);
return total > 0 ? round2((top / total) * 100) : 0;
}
function buildRecommendations(input: {
overloadedCount: number;
underutilizedCount: number;
averageWorkloadScore: number;
top3LoadSharePercent: number;
highPendingReviewContributors: { userName: string; pendingReviews: number }[];
topContributors: { userName: string; workloadScore: number }[];
increasingTrendCount: number;
decreasingTrendCount: number;
}): WorkloadRecommendation[] {
const recommendations: WorkloadRecommendation[] = [];
if (input.overloadedCount > 0) {
const names = input.topContributors.slice(0, 2).map((item) => item.userName).join(", ");
recommendations.push({
priority: "high",
title: "Rebalance overloaded contributors",
rationale: `${input.overloadedCount} contributor(s) are above workload threshold (>=80).`,
action: names
? `Reduce review/issue load for ${names} by shifting assignments to lower-load contributors.`
: "Shift reviews and issue assignments to lower-load contributors.",
});
}
if (input.highPendingReviewContributors.length > 0) {
const targets = input.highPendingReviewContributors
.slice(0, 3)
.map((entry) => `${entry.userName} (${entry.pendingReviews})`)
.join(", ");
recommendations.push({
priority: "high",
title: "Clear review queue bottlenecks",
rationale: "Pending review queue concentration indicates potential PR latency risk.",
action: `Prioritize review support for: ${targets}.`,
});
}
if (input.top3LoadSharePercent >= 55) {
recommendations.push({
priority: "medium",
title: "Diversify review ownership",
rationale: `Top 3 contributors carry ${input.top3LoadSharePercent}% of workload score.`,
action: "Broaden CODEOWNERS/reviewer pool to distribute review demand across more contributors.",
});
}
if (input.underutilizedCount > 0 && input.overloadedCount > 0) {
recommendations.push({
priority: "medium",
title: "Pair low-load and high-load contributors",
rationale: "Both overloaded and underutilized contributors are present.",
action: "Rotate issue ownership and shadow-review tasks toward underutilized team members.",
});
}
if (input.increasingTrendCount > input.decreasingTrendCount && input.averageWorkloadScore >= 50) {
recommendations.push({
priority: "low",
title: "Plan short-term capacity buffer",
rationale: "More contributors show increasing workload trend than decreasing trend.",
action: "Limit new concurrent initiatives or delay non-critical review-heavy work in the next sprint.",
});
}
if (!recommendations.length) {
recommendations.push({
priority: "low",
title: "Workload distribution is healthy",
rationale: "No strong overload bottlenecks detected from current repository signals.",
action: "Maintain current reviewer rotation and monitor trends weekly.",
});
}
return recommendations;
}
export const GET: APIRoute = withErrorHandler(async ({ params, url, locals }) => {
const owner = params.owner;
const repoName = params.repo;
if (!owner || !repoName) return badRequest("Missing route parameters");
const parsed = parseQuery(url, querySchema);
if ("error" in parsed) return parsed.error;
const repository = await resolveRepository(owner, repoName);
if (!repository) return notFound("Repository not found");
if (!(await canReadRepo(locals.user?.id, repository, { isAdmin: locals.user?.isAdmin }))) {
return notFound("Repository not found");
}
const days = parsed.data.days ?? 30;
const limit = parsed.data.limit ?? 50;
const workloads = await getDeveloperWorkloads({
repositoryId: repository.id,
days,
});
const totalScore = workloads.reduce((sum, entry) => sum + entry.workloadScore, 0);
const averageScore = workloads.length > 0 ? round2(totalScore / workloads.length) : 0;
const overloadedCount = workloads.filter((w) => w.workloadScore >= 80).length;
const underutilizedCount = workloads.filter((w) => w.workloadScore <= 20).length;
const increasingTrendCount = workloads.filter((w) => w.trend === "increasing").length;
const stableTrendCount = workloads.filter((w) => w.trend === "stable").length;
const decreasingTrendCount = workloads.filter((w) => w.trend === "decreasing").length;
const workloadScores = workloads.map((entry) => entry.workloadScore);
const p50 = percentile(workloadScores, 50);
const p90 = percentile(workloadScores, 90);
const top3LoadSharePercent = topShare(workloadScores, 3);
const highPendingReviewContributors = workloads
.filter((entry) => entry.pendingReviews >= 3)
.slice(0, 5)
.map((entry) => ({
userName: entry.userName,
pendingReviews: entry.pendingReviews,
}));
const recommendations = buildRecommendations({
overloadedCount,
underutilizedCount,
averageWorkloadScore: averageScore,
top3LoadSharePercent,
highPendingReviewContributors,
topContributors: workloads.slice(0, 5).map((entry) => ({
userName: entry.userName,
workloadScore: entry.workloadScore,
})),
increasingTrendCount,
decreasingTrendCount,
});
return success({
repositoryId: repository.id,
days,
summary: {
totalContributors: workloads.length,
averageWorkloadScore: averageScore,
overloadedCount,
underutilizedCount,
},
trendIntelligence: {
increasingTrendCount,
stableTrendCount,
decreasingTrendCount,
p50WorkloadScore: p50,
p90WorkloadScore: p90,
top3LoadSharePercent,
},
recommendations,
contributors: workloads.slice(0, limit),
});
});